package com.google.gson.internal.bind;

import com.google.gson.JsonException;
import com.google.gson.reflect.TypeToken;

import java.lang.reflect.*;

public class SimpleObjectConstructor<T> implements ObjectConstructor<T> {

    private TypeToken<T> typeToken;

    private Class<T> clazz;

    private Object unsafe;

    private Method allocateInstance;

    public SimpleObjectConstructor(TypeToken<T> type) {
        this.typeToken = type;
        this.clazz = (Class<T>) type.getRawType();

        // 創建unsafe類構建器, 繞過java安全機制, 類似c語言中的malloc
        createUnsafe();
    }

    @Override
    public T construct() {
        try {
            checkInstantiable(clazz);
            return (T) allocateInstance.invoke(unsafe, clazz);
        } catch (Exception e) {
            throw new JsonException("Unable to create the instance " + clazz.getName(), e);
        }
    }

    private void createUnsafe() {
        try {
            Class<?> unsafeClass = Class.forName("sun.misc.Unsafe");
            Field unsafeField = unsafeClass.getDeclaredField("theUnsafe");
            unsafeField.setAccessible(true);
            unsafe = unsafeField.get(null);
            allocateInstance = unsafeClass.getMethod("allocateInstance", Class.class);
        } catch (Exception ignore) {}
    }

    /**
     * Check if the class can be instantiated by Unsafe allocator. If the instance has interface or abstract modifiers
     * return an exception message.
     * @param c instance of the class to be checked
     * @return if instantiable {@code null}, else a non-{@code null} exception message
     */
    private String checkInstantiable(Class<?> c) {
        int modifiers = c.getModifiers();
        if (Modifier.isInterface(modifiers)) {
            return "Interfaces can't be instantiated! Register an InstanceCreator "
                    + "or a TypeAdapter for this type. Interface name: " + c.getName();
        }
        if (Modifier.isAbstract(modifiers)) {
            return "Abstract classes can't be instantiated! Register an InstanceCreator "
                    + "or a TypeAdapter for this type. Class name: " + c.getName();
        }
        return null;
    }
}
